#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <vector>

#include <boost/lexical_cast.hpp>
#include <boost/thread/xtime.hpp>

#include <thrift/Thrift.h>
#include <thrift/transport/TServerTransport.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/protocol/TProtocol.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>

#include "SearchEngine.h"

#include "base/logging.h"
#include "fcgi_service/fcgi_service.h"

#include "search_engine/search_index.h"

using namespace std;
using namespace fcgi;
using namespace boost;
using namespace apache::thrift;

class SearchRequest: public Request {
public:
  SearchRequest(FCGX_Request* r) : Request(r) {}
  virtual bool Response() {
    if(!fcgi_out_) {
      cout << "fcgi_out null";
      return false;
    }

    stringstream rsp_header;
    rsp_header << "Content-type: text/html; charset=utf-8\r\n"
               << "\r\n";
    FCGX_PutS(rsp_header.str().c_str(), fcgi_out_);

    string rsp_body;

    const std::string & query = GetQuery("q");
    int start = GetQueryInt("s", 0);
    int count = GetQueryInt("c", 10);
    vector<string> seg_words;
    int32_t total_count = 0;
    vector<int> ids = SearchIndex::Instance().Search(query, start, count, &seg_words, &total_count);
    // vector<int> ids = SearchIndex::Instance().Search(query, 0, 10);
    // vector<int> ids = SearchIndex::Instance().Search("招商银行投资", 0, 10);
    cout << query << " " << start << " " << count << " return size : " << ids.size() << endl;
    for(size_t i = 0; i < ids.size(); ++i) {
      string sid = boost::lexical_cast<string>(ids[i]);
      rsp_body += "<a href=\"http://www.slowrose.com/a/" + sid + ".html\">" + sid + "</a></br>";
    }

    FCGX_PutS(rsp_body.c_str(), fcgi_out_);
    return true;
  }
};

class SearchRequestFactory : public RequestFactory {
public:
  virtual RequestPtr Create(FCGX_Request * r) {
    char * path = FCGX_GetParam("SCRIPT_NAME", r->envp);
    RequestPtr req;
    if (path) {
      if (strcmp(path, "/search") == 0) {
        req = RequestPtr(new SearchRequest(r));
      }
    }
    return req;
  }
};

void TestSingleTokenSearch() {
  SearchIndex & inst = SearchIndex::Instance();
  // inst.Dump();
  // vector<int> ids = inst.Search("这是一篇不错的投资文章", 0, 10);
  // vector<int> ids = inst.Search("投资", 0, 10);
  
  string text("银行");
  int32_t total_count = 0;
  map<double, string> m = inst.GetWeightedTokens(text);
  cout << "++++++" << m.size() << endl;
  for(map<double, string>::iterator it = m.begin(); it != m.end(); ++it) {
    cout << it->first << " -" << it->second << "-" << endl;
  }

  // return 0;
  vector<string> seg_words; 
  vector<int> ids = inst.Search("银行", 0, 10, &seg_words, &total_count);

  cout << ids.size() << " --------:" << endl;
  for (size_t i = 0; i < ids.size(); ++i) {
    cout << ids[i] << endl;
  }
  seg_words.clear();
  ids = inst.Search("银行", 5, 10, &seg_words, &total_count);
  cout << ids.size() << " --------:" << endl;
  for (size_t i = 0; i < ids.size(); ++i) {
    cout << ids[i] << endl;
  }

  seg_words.clear();
  ids = inst.Search("银行", 10, 10, &seg_words, &total_count);
  cout << ids.size() << " --------:" << endl;
  for (size_t i = 0; i < ids.size(); ++i) {
    cout << ids[i] << endl;
  }
}

void TestMultiWordSearch() {
  SearchIndex & inst = SearchIndex::Instance();
  vector<string> seg_words; 
  int32_t total_count = 0;
  vector<int> ids = inst.Search("招商银行投资", 0, 10, &seg_words, &total_count);
  cout << ids.size() << " --------:" << endl;
  for (size_t i = 0; i < ids.size(); ++i) {
    cout << ids[i] << endl;
  }

  seg_words.clear();
  ids = inst.Search("招商银行投资", 5, 10, &seg_words, &total_count);
  cout << ids.size() << " --------:" << endl;
  for (size_t i = 0; i < ids.size(); ++i) {
    cout << ids[i] << endl;
  }

  seg_words.clear();
  ids = inst.Search("招商银行投资", 10, 10, &seg_words, &total_count);
  cout << ids.size() << " --------:" << endl;
  for (size_t i = 0; i < ids.size(); ++i) {
    cout << ids[i] << endl;
  }
}

class SearchEngineI : virtual public SearchEngineIf {
 public:
  virtual void Search(SearchResult& _return, const std::string& text, const int32_t start, const int32_t count) {
    SearchIndex & inst = SearchIndex::Instance();
    cout << "text : " << text << endl;
    _return.article_ids = inst.Search(text, start, count, &(_return.seg_words), &(_return.total_count));
  }
  virtual void SearchByTime(SearchResult& _return, const std::string& text, const int32_t start, const int32_t count) {
    SearchIndex & inst = SearchIndex::Instance();
    cout << "text : " << text << endl;
    _return.article_ids = inst.SearchByTime(text, start, count, &(_return.seg_words), &(_return.total_count));
  }
};
int main() {
  LOG_INIT("./search.log", "DEBUG");
  // TestSingleTokenSearch();
  // TestMultiWordSearch();
  SearchIndex::Instance().Reload("./search-index/");
  SearchIndex::Instance().Dump();

  FcgiServer * fcgi_server = new FcgiServer("127.0.0.1:10092", 16);
  fcgi_server->RegisterRequestFactory(RequestFactoryPtr(new SearchRequestFactory()));
  fcgi_server->Start(false);

  int port = 10093;
  cout << "thrift listens on port " << port << endl;
  shared_ptr<SearchEngineI> handler(new SearchEngineI());
  shared_ptr<TProcessor> processor(new SearchEngineProcessor(handler));
  shared_ptr<transport::TServerTransport> serverTransport(new transport::TServerSocket(port));
  shared_ptr<transport::TTransportFactory> transportFactory(new transport::TBufferedTransportFactory());
  // shared_ptr<transport::TTransportFactory> transportFactory(new transport::TFramedTransportFactory());
  shared_ptr<protocol::TProtocolFactory> protocolFactory(new protocol::TBinaryProtocolFactory());
  server::TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
  server.serve();
  return 0;
}

